﻿#region NeoPatterns
// 
// NeoPatterns framework is released under LGPLv3 license.
// See http://www.opensource.org/licenses/lgpl-3.0.html for more details.
// 
// Please visit http://neopatterns.wikidot.com for more information about NeoPatterns
// 
// Copyright (c) Pascal Craponne 2008
// 
#endregion

using System;
using System.Reflection;
using NeoPatterns.Exception;

namespace NeoPatterns.Utility
{
    /// <summary>
    /// Extension class to help MemberInfo
    /// </summary>
    public static class MemberInfoExtension
    {
        /// <summary>
        /// Returns MemberInfo field/property type
        /// </summary>
        /// <param name="memberInfo">field/property</param>
        /// <returns></returns>
        public static Type GetMemberType(this MemberInfo memberInfo)
        {
            var propertyInfo = memberInfo as PropertyInfo;
            if (propertyInfo != null)
                return propertyInfo.PropertyType;
            var fieldInfo = memberInfo as FieldInfo;
            if (fieldInfo != null)
                return fieldInfo.FieldType;
            throw new ArgumentException(memberInfo.GetType().Name);
        }

        /// <summary>
        /// Assigns value to field/property
        /// </summary>
        /// <param name="memberInfo">field/property</param>
        /// <param name="owner">field/property owner object</param>
        /// <param name="value">value to assign</param>
        public static void SetMemberValue(this MemberInfo memberInfo, object owner, object value)
        {
            var propertyInfo = memberInfo as PropertyInfo;
            if (propertyInfo != null)
            {
                var setMethod = propertyInfo.GetSetMethod(true);
                if (setMethod == null)
                    throw new MissingAccessorException();
                setMethod.Invoke(owner, new[] { value });
                return;
            }
            var fieldInfo = memberInfo as FieldInfo;
            if (fieldInfo != null)
            {
                fieldInfo.SetValue(owner, value);
                return;
            }
            throw new ArgumentException(memberInfo.GetType().Name);
        }

        /// <summary>
        /// Reads value from a field/property
        /// </summary>
        /// <param name="memberInfo"></param>
        /// <param name="owner"></param>
        /// <returns></returns>
        public static object GetMemberValue(this MemberInfo memberInfo, object owner)
        {
            var propertyInfo = memberInfo as PropertyInfo;
            if (propertyInfo != null)
            {
                var setMethod = propertyInfo.GetGetMethod(true);
                return setMethod.Invoke(owner, null);
            }
            var fieldInfo = memberInfo as FieldInfo;
            if (fieldInfo != null)
            {
                return fieldInfo.GetValue(owner);
            }
            throw new ArgumentException(memberInfo.GetType().Name);
        }

        /// <summary>
        /// Returns a custom attribute for the MemberInfo, or null if none
        /// </summary>
        /// <typeparam name="TAttribute">Attribute type</typeparam>
        /// <param name="memberInfo">field/property</param>
        /// <param name="inherited">If true, specifies to also search the ancestors of element for custom attributes</param>
        /// <returns>Attribute or null</returns>
        public static TAttribute GetCustomAttribute<TAttribute>(this MemberInfo memberInfo, bool inherited)
            where TAttribute : Attribute
        {
            return (TAttribute)Attribute.GetCustomAttribute(memberInfo, typeof(TAttribute), inherited);
        }
    }
}